(function(win, $, console) {
    "use strict";
    var Z = win.Z, doc = win.document, BundleList, alert, confirm, tip, loading;

    function Base() {
        var o = arguments[0] || {};
        this.events = o.events || [];
        this.selector = o.selector || '';
        this.stay = o.stay || false;
        this.data = o.data || {};
        this.installed = false;
        this.bound = false;
    }

    Base.prototype = {
        'open': function() {
            if (this.installed) {
                this.wake();
            } else {
                this.install();
            }
            if (this.onopen !== undefined) {
                this.onopen();
            }
            return this;
        },
        'close': function() {
            if (this.stay) {
                this.sleep();
            } else {
                this.remove();
            }
            if (this.onclose !== undefined) {
                this.onclose();
            }
        },
        'install': function() {
            this.rend();
            this.bindEvents();
            if (this.oninstall !== undefined) {
                this.oninstall();
            }
            this.installed = true;
        },
        'remove': function() {
            $(this.selector).remove();
            this.unbindEvents();
            if (this.onremove !== undefined) {
                this.onremove();
            }
            this.installed = false;
        },
        'sleep': function() {
            $(this.selector).hide();
            this.unbindEvents();
            if (this.onsleep !== undefined) {
                this.onsleep();
            }
        },
        'wake': function() {
            $(this.selector).show();
            this.bindEvents();
            if (this.onwake !== undefined) {
                this.onwake();
            }
        },
        'addEvent': function() {
            this.events.push(arguments);
        },
        'bindEvents': function() {
            var i, l, e, len, target;
            if (this.bound) {
                console.warn('rebind event');
                return;
            }

            for (i = 0, l = this.events.length; i < l; i += 1) {
                e = this.events[i];
                len = e.length;
                target = e[0].constructor === String ? this.find(e[0]) : $(e[0]);
                if (len === 3) {
                    target.bind(e[1], e[2]);
                } else if (len === 4) {
                    target.on(e[1], e[2], e[3]);
                }
            }
            this.bound = true;
        },
        'unbindEvents': function() {
            var i, l, e, len, target;
            if (!this.bound) {
                console.log('do not need unbind');
                return;
            }
            for (i = 0, l = this.events.length; i < l; i += 1) {
                e = this.events[i];
                len = e.length;
                target = e[0].constructor === String ? this.find(e[0]) : $(e[0]);
                if (len === 3) {
                    target.unbind(e[1], e[2]);
                } else if (len === 4) {
                    target.off(e[1], e[2], e[3]);
                }
            }
            this.bound = false;
        },
        'find': function(s) {
            return $(this.selector + ' ' + s);
        }
    };

    function Bundle() {
        Base.apply(this, arguments);
        var o = arguments[0] || {},
            params = Bundle.getParams(),
            tmpl = o.tmpl || '';
        this.symbol = params[0];
        this.data = o.data || '';
        this.params = params.length > 1 ? params.slice(1) : [];
        this.selector = '#right div.tmpl-' + this.symbol;
        this.tmpl = Z.url('tmpl/' + tmpl + '.html');
    }

    Bundle.prototype = new Base();

    Bundle.getParams = function(href) {
        var hash = href || win.location.hash,
            segments = ['profile'],
            r = /^javascript\:/;
        if (hash.length > 1 && !r.test(hash)) {
            segments = hash.substring(1).split('#');
        } else {
            win.location.href = Z.url('#' + segments[0]);
        }
        return segments;
    };

    Bundle.prototype.install = function() {
        this.rend();
        if (this.oninstall !== undefined) {
            this.oninstall();
        }
        this.installed = true;
    };

    Bundle.prototype.rend = function() {
        if (this.tmpl === '') {
            return;
        }
        var self = this;
        $.ajax({
            'url': self.tmpl,
            'dataType': 'html',
            'success': function(html) {
                var content = $('<div class="tmpl-' + self.symbol + '">' + html + '</div>');
                $('div#right').append(content);
                self.onload(html, self.data);
                self.bindEvents();
            },
            'error': function() {
                Z.alert('load tmpl failed.');
            }
        });
    };

    BundleList = {
        'list': {},
        'current': '',
        'load': function(symbol) {
            var self = this;
            if (this.list[this.current]) {
                this.list[this.current].sleep();
            }
            if (this.list[symbol]) {
                this.list[symbol].wake();
            } else {
                Z.loadScript(symbol, function() {
                    self.list[symbol].open();
                });
            }
            this.current = symbol;
        },
        'append': function(bundle) {
            if (this.list[bundle.symbol]) {
                Z.alert(bundle.symbol + ' is exsits.');
            } else {
                this.list[bundle.symbol] = bundle;
            }
        }
    };

    function Loader() {
        Base.apply(this, arguments);
        var o = arguments[0] || {}, self = this;
        this.url = o.url;
        this.data = o.data || {};
        this.data.page = parseInt(this.data.page) || 1;
        this.data.pageSize = parseInt(this.data.pageSize) || 20;
        this.listTmpl = o.listTmpl;
        this.emptyTmpl = o.emptyTmpl || '';
        this.list = o.list;
        this.pagination = o.pagination;
        this.addEvent(this.pagination, 'click', 'a', function() {
            var page = $(this).attr("page");
            self.goto(page);
            return false;
        });

        this.bindEvents();
    }

    Loader.prototype = new Base();

    Loader.prototype.load = function(data) {
        var self = this;
        $.extend(this.data, data || {});
        Z.ajax({
            url: this.url,
            data: this.data,
            success: function(data) {
                self.success(data);
            }
        });
    };

    Loader.prototype.success = function(data) {
        if (data.total === undefined) {
            this.rendList(data);
        } else {
            if (data.total > 0) {
                if (data.records.length > 0 && this.data.page > 0) {
                    this.rendList(data);
                } else {
                    this.prev();
                }
                this.rendPagination(data.total);
            } else {
                $(this.list).html(this.emptyTmpl || '');
            }
        }
        if (this.onload !== undefined) {
            this.onload(data);
        }
    };

    Loader.prototype.error = function(jqXHR, status, errorThrown) {
        Z.log(jqXHR, status, errorThrown);
    };

    Loader.prototype.goto = function(page) {
        this.load({
            page: parseInt(page)
        });
    };

    Loader.prototype.next = function() {
        this.data.page = parseInt(this.data.page) + 1;
        this.load({
            page: this.data.page
        });
    };

    Loader.prototype.prev = function() {
        this.data.page = parseInt(this.data.page) - 1;
        this.load({
            page: this.data.page
        });
    };

    Loader.prototype.reload = function(flag) {
        this.goto(flag ? 1 : this.data.page);
    };

    Loader.prototype.rendList = function(data) {
        var content = win.tmpl(this.listTmpl, data);
        this.list.html(content);
    };

    Loader.prototype.rendPagination = function(total) {
        var pagination;
        if (this.pagination === undefined) {
            return;
        }
        if (this.data.pageSize < total) {
            pagination = Loader.genPagination(total, this.data.pageSize, this.data.page);
            $(this.pagination).html(pagination).show();
        } else {
            $(this.pagination).hide();
        }
    };

    Loader.genPagination = function(total, pageSize, page) {
        page = parseInt(page);
        total = parseInt(total);
        pageSize = parseInt(pageSize);
        var i, next,
            totalPage = Math.ceil(total / pageSize),
            pagination = [],
            info = '<ul class="pagination"><li class="info"> 共${0}条 第${1}页/共${2}页</li>'.parse([total, page, totalPage]);
        pagination.push(info);
        if (page === 1) {
            pagination.push('<li title="«" class="prev active">prev</li>');
        } else {
            pagination.push('<li class="prev"><a title="«" href="javascript:void(0);" page="' + (page - 1) + '">prev</a></li>');
        }

        if (totalPage > 10) {
            if (page > 7 && page < (totalPage - 6)) {
                pagination.push('<li><a href="javascript:void(0);" page="1">1</a></li>');
                pagination.push('<li>...</li>');
                for (i = (page - 5); i < (page + 6); i += 1) {
                    if (i === page) {
                        pagination.push('<li class="active">' + i + '</li>');
                    } else {
                        pagination.push('<li><a href="javascript:void(0);" page="' + i + '">' + i + '</a></li>');
                    }
                }
                pagination.push('<li>...</li>');
                pagination.push('<li><a href="javascript:void(0);" page="' + totalPage + '">' + totalPage + '</a></li>');
            } else if (page <= 7) {
                for (i = 1; i < 11; i += 1) {
                    if (i === page) {
                        pagination.push('<li class="active">' + i + '</li>');
                    } else {
                        pagination.push('<li><a href="javascript:void(0);" page="' + i + '">' + i + '</a></li>');
                    }
                }
                pagination.push('<li class="ellipsis">...</li>');
                pagination.push('<li><a href="javascript:void(0);" page="' + totalPage + '">' + totalPage + '</a></li>');
            } else {
                pagination.push('<li><a href="javascript:void(0);" page="1">1</a></li>');
                pagination.push('<li class="ellipsis">...</li>');
                for (i = (totalPage - 9); i <= totalPage; i += 1) {
                    if (i === page) {
                        pagination.push('<li class="active">' + i + '</li>');
                    } else {
                        pagination.push('<li><a href="javascript:void(0);" page="' + i + '">' + i + '</a></li>');
                    }
                }
            }
        } else {
            for (i = 1; i < totalPage + 1; i += 1) {
                if (i === page) {
                    pagination.push('<li class="active">' + i + '</li>');
                } else {
                    pagination.push('<li><a href="javascript:void(0);" page="' + i + '">' + i + '</a></li>');
                }
            }
        }

        if (page === totalPage) {
            pagination.push('<li class="next active">next</li>');
        } else {
            pagination.push('<li class="next"><a  title="»" href="javascript:void(0);" page="' + (page + 1) + '">next</a></li>');
        }
        pagination.push('</ul>');
        return pagination.join('');
    };


    function Dialog() {
        Dialog.counter += 1;
        Base.apply(this, arguments);

        var o = arguments[0] || {}, self = this, closeHandle;
        this.id = 'z-dialog-' + Dialog.counter;
        this.data = o.data || {};
        this.title = o.title || '';
        this.content = o.content || '';
        this.width = o.width || 300;
        this.height = (o.height || 'auto') === 'auto' ? 'auto' : (o.height + 'px');
        this.opacity = o.opacity || 0.3;
        this.selector = '#' + this.id;
        this.stay = o.stay || false;
        this.closable = o.closable || true;
        this.tmpl = Dialog.tmpl.parse(this, '');

        if (this.closable) {
            closeHandle = function(e) {
                if ((e.type === 'keypress' && e.keyCode === 113) || e.type === 'click') {
                    if (self.stay) {
                        self.sleep();
                    } else {
                        self.close();
                    }
                }
            };

            this.addEvent('.z-header a.z-close', 'click', closeHandle);
            this.addEvent($(doc), 'keypress', closeHandle);
        }
    }

    Dialog.prototype = new Base();

    Dialog.tmpl = '<div class="z-dialog" id="${id}">\
        <div class="z-dialog-background" style="opacity:${opacity};"></div>\
        <div class="z-dialog-content" style="width:${width}px;height:${height};overflow:auto;">\
            <div class="z-header">\
                <div class="z-title">${title}</div>\
                <a class="z-close" href="javascript:void(0);" title="按Q键关闭">X</a>\
            </div>\
            <div class="z-content">${content}</div>\
        </div>\
    </div>';

    Dialog.counter = 0;

    Dialog.prototype.rend = function() {
        $('body').append($(this.tmpl));
        $(this.selector).show();
        this.align();
    };

    Dialog.prototype.align = function() {
        $(this.find('.z-dialog-content')).css({
            "margin-left": -($(this.find('.z-dialog-content')).width() / 2) + "px",
            "margin-top": -($(this.find('.z-dialog-content')).height() / 2) + "px"
        });
    };


    function Form() {
        Dialog.apply(this, arguments);
        var o = arguments[0] || {}, self = this, submit;
        this.action = o.action || '';
        this.method = o.method || 'post';
        this.stay = o.stay || true;
        submit = function() {
            Z.ajax({
                'url': Z.url(self.action),
                'data': $(this).serialize(),
                'type': self.method,
                'success': function(data) {
                    self.sleep();
                    if (self.onsubmit !== undefined) {
                        self.onsubmit(data);
                    }
                }
            });
            return false;
        };

        this.events.push(['form', 'submit', submit]);
    }

    Form.prototype = new Dialog();

    Form.prototype.fill = function(data) {
        this.find('select').each(function() {
            var name = $(this).attr('name');
            $(this).attr('value', data[name] || '');
            $(this).val(data[name] || '');
        });

        this.find('input[type="text"],input[type="hidden"],textarea').each(function() {
            var name = $(this).attr('name');
            $(this).val(data[name] || '');
        });

        this.find(":radio,:checkbox").each(function() {
            var name = $(this).attr('name'),
                value = $(this).attr('value');
            if ((data[name] || 0) === value) {
                $(this).attr("checked", "checked");
            }
        });
        return this;
    };

    Form.prototype.clear = function() {
        this.find(":radio,:checkbox").each(function() {
            $(this).removeAttr("checked");
        });

        this.find('input[type="text"],input[type="hidden"],textarea,select').each(function() {
            $(this).val('');
        });

        return this;
    };

    /**
     * @param Element t target
     * @param Dict d data
     * @param Function f callback parse item 
     */
    Form.fillSelect = function(t, d, f) {
        var h = [], p, r;
        for (p in d) {
            if (d.hasOwnProperty(p)) {
                if (f === undefined) {
                    r = [p, d[p]];
                } else {
                    r = f(p, d[p]);
                }
                h.push('<option value="${0}">${1}</option>'.parse(r));
            }
        }
        t.html(h.join(''));
        if (t.attr('value') !== '') {
            t.val(t.attr('value'));
        }
    };

    /*  Z tools  */
    alert = function(content) {
        var dialog = new Dialog({
            'title': 'MsgBox',
            'content': content
        });
        dialog.open();
        return dialog;
    };

    confirm = function(content, confirm, cancel) {
        var ui = content + '<div class="z-bottom"><a class="z-confirm btn" href="javascript:void(0);">确定</a><a class="z-cancel btn" href="javascript:void(0);">取消</a></div>',
            dialog = new Dialog({'content': ui});

        dialog.addEvent('a.z-confirm', 'click', function() {
            if (confirm !== undefined) {
                dialog.onclose = confirm;
            }
            dialog.close();
            return false;
        });

        dialog.addEvent('a.z-cancel', 'click', function() {
            if (confirm !== undefined) {
                dialog.onclose = cancel;
            }
            dialog.close();
            return false;
        });

        dialog.addEvent($(doc), 'keypress', function(e) {
            if (e.keyCode === 13) {
                $(dialog.find('a.z-confirm')).trigger('click');
            }
        });
        dialog.open();
        return dialog;
    };

    tip = function(content, seconds, callback) {
        var dialog = new Dialog({'content': content}), timeout;
        dialog.open();
        timeout = win.setTimeout(function() {
            if (callback !== undefined) {
                callback(dialog);
            }
            dialog.close();
            win.clearTimeout(timeout);
        }, (seconds || 1.5) * 1000);
    };


    loading = function(content) {
        var ui = (content === undefined ? '' : content) + '<img src="img/loading.gif" alt="loading..."/>',
            dialog = new Dialog({'title': '加载中……', 'content': ui});
        dialog.open();
        return dialog;
    };

    Z.Base = Base;
    Z.Dialog = Dialog;
    Z.Form = Form;
    Z.Loader = Loader;
    Z.Bundle = Bundle;
    Z.BundleList = BundleList;
    Z.alert = alert;
    Z.confirm = confirm;
    Z.tip = tip;
    Z.loading = loading;
})(window, $, console);